package com.lipy.caffeine;

import com.github.benmanes.caffeine.cache.*;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;

import java.io.PushbackInputStream;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @Description Caffeine user
 * @Date 2021/2/3 15:04
 * @Author by lipye
 */
public class UseTest {
    public static void main(String[] args) {
//        manual();
//        load();
//        asynchronously();
//        baseTime();
//        expireAfter();
        remove();
    }

    /**
     * removalCase
     *  EXPLICIT  -- invalidate
     *  REPLACED  -- update
     *  COLLECTED
     *  EXPIRED  -- time expire
     *  SIZE  --- maxsize
     *
     *  estimatedSize 不包含驱逐的
     */


    public static void manual() {
        Cache<Object, Object> cache = Caffeine.newBuilder()
                .expireAfterWrite(3, TimeUnit.MINUTES)
                .maximumSize(10_000)
                .build();
        Object a = cache.getIfPresent("a");
        System.out.println("a = " + a);
        Object b1 = cache.get("b", b -> {
            return "value of " + b;
        });
        System.out.println("b1 = " + b1);
        cache.put("c", 1);
        System.out.println("cache c= " + cache.getIfPresent("c"));
        @NonNegative long l =
                cache.estimatedSize();
        System.out.println("l = " + l);
        ConcurrentMap<Object, Object> objectObjectConcurrentMap = cache.asMap();
        System.out.println("objectObjectConcurrentMap = " + objectObjectConcurrentMap);
        cache.cleanUp();
        System.out.println(cache.estimatedSize());
    }


    public static void load() {
        LoadingCache<Object, String> loadingCache = Caffeine.newBuilder()
                .maximumSize(10_000)
                .expireAfterWrite(1, TimeUnit.MINUTES)
                .build(k -> createExpensiveGraph(k)); // 查询并在缺失的情况下使用同步的方式来构建一个缓存
        String a = loadingCache.get("a");
        System.out.println("a = " + a);

        List<String> list = new ArrayList<>();
        list.add("b");
        list.add("c");
        Map<Object, String> all = loadingCache.getAll(list);
        System.out.println("all = " + all);
    }

    public static String createExpensiveGraph(Object key){
        return "default "+ key;
    }
    public static void asynchronously(){
        AsyncLoadingCache<String, Object> asyncLoadingCache = Caffeine.newBuilder()
                .maximumSize(10_000)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                // Either: Build with a synchronous computation that is wrapped as asynchronous
                .buildAsync(key -> createExpensiveGraph(key));// 查询并在缺失的情况下使用异步的方式来构建一个缓存

        String key = "name1";

// 查询并在缺失的情况下使用异步的方式来构建缓存
        CompletableFuture<Object> graph = asyncLoadingCache.get(key);
// 查询一组缓存并在缺失的情况下使用异步的方式来构建缓存
        List<String> keys = new ArrayList<>();
        keys.add(key);
        CompletableFuture<Map<String, Object>> graphs = asyncLoadingCache.getAll(keys);
        try {
            System.out.println("graph = " + graph.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

//        默认使用ForkJoinPool.commonPool()来执行异步线程，但是我们可以通过Caffeine.executor(Executor) 方法来替换线程池。
// 异步转同步
        @NonNull LoadingCache<String, Object> loadingCache = asyncLoadingCache.synchronous();
    }

//    maximumWeight与maximumSize不可以同时使用

    public static void baseTime(){
        // Evict based on a fixed expiration policy
// 基于固定的到期策略进行退出
        LoadingCache graphs = Caffeine.newBuilder()
                .expireAfterAccess(2, TimeUnit.SECONDS)
                .removalListener((key,val,cause) -> {
                    System.out.printf("Key %s was removed (%s)%n", key, cause);
                })
                .build(key -> createExpensiveGraph(key));
        LoadingCache graphs2 = Caffeine.newBuilder()
                .expireAfterWrite(4, TimeUnit.SECONDS)
                .removalListener((key,val,cause) -> {
                    System.out.printf("Key %s was removed (%s)%n", key, cause);
                })
                .build(key -> createExpensiveGraph(key));


        graphs.put("a","a1");
        graphs2.put("b","b2");
        graphs.invalidate("a");
        Object a = graphs.get("a");
        Object b = graphs2.get("b");
        System.out.println("a = " + a);
        System.out.println("b =" +b);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        a = graphs.get("a");
        b = graphs2.get("b");

        System.out.println(graphs.estimatedSize());
        System.out.println(graphs.estimatedSize());
        System.out.println("a = " + a);
        System.out.println("b =" +b);
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(graphs.estimatedSize());
        System.out.println(graphs.estimatedSize());
        a = graphs.get("a");
        b = graphs2.get("b");
        System.out.println("a = " + a);
        System.out.println("b =" +b);

        System.out.println("a = " + a);
    }

    public static void expireAfter(){
// Evict based on a varying expiration policy
// 基于不同的到期策略进行退出
        LoadingCache graphs3 = Caffeine.newBuilder()
                .expireAfter(new Expiry<Object, Object>() {
                    @Override
                    public long expireAfterCreate(@NonNull Object o, @NonNull Object o2, long currentTime) {
                        return TimeUnit.SECONDS.toNanos(3);
                    }

                    @Override
                    public long expireAfterUpdate(@NonNull Object o, @NonNull Object o2, long currentTime, @NonNegative long currentDuration) {
                        return TimeUnit.SECONDS.toNanos(2);
                    }

                    @Override
                    public long expireAfterRead(@NonNull Object o, @NonNull Object o2, long currentTime, @NonNegative long currentDuration) {
                        return TimeUnit.SECONDS.toNanos(1);
                    }
                })
                // removalcase 类型
                .removalListener((k,v,c) -> {
                    System.out.printf("k = %s, v = %s, c = %s" , k,v,c);
                    System.out.println();
                } )
                .build(key -> createExpensiveGraph(key));

        graphs3.put("create","3");
        graphs3.put("update","2");
        graphs3.put("read","1");

        sleepMs(2500);
//        System.out.println("graphs3.get(\"create\") = " + graphs3.get("create"));
        System.out.println("graphs3.get(\"update\") = " + graphs3.get("update"));
        System.out.println("graphs3.get(\"read\") = " + graphs3.get("read"));

        graphs3.put("update","22");
        sleepMs(600);
        System.out.println("graphs3.get(\"create\") = " + graphs3.get("create"));
        System.out.println("graphs3.get(\"update\") = " + graphs3.get("update"));
        System.out.println("graphs3.get(\"read\") = " + graphs3.get("read"));

        sleepMs(1200);
        System.out.println("graphs3.get(\"create\") = " + graphs3.get("create"));
        System.out.println("graphs3.get(\"update\") = " + graphs3.get("update"));
        System.out.println("graphs3.get(\"read\") = " + graphs3.get("read"));

    }

    private static void sleepMs(long time) {
        try {
            TimeUnit.MILLISECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

   public static void remove(){
       LoadingCache<Object, String> build = Caffeine.newBuilder()
               .removalListener((k, v, c) -> {
                   System.out.printf("k = %s, v = %s, c = %s", k, v, c);
                   System.out.println();
               })
               .expireAfterAccess(2,TimeUnit.SECONDS)
               .expireAfterWrite(1,TimeUnit.SECONDS)
               .maximumSize(2)
               .build(key -> createExpensiveGraph(key));
       build.put("a","av");
       build.put("b","bv");
       build.put("c","cv");
       sleepMs(500);
       System.out.println("build a= " + build.get("a"));
       System.out.println("build b= " + build.get("b"));
       System.out.println(build.estimatedSize());
       build.invalidate("b");
       sleepMs(2000);
//       System.out.println("build a= " + build.get("a"));
//       System.out.println("build b= " + build.get("b"));
       System.out.println(build.estimatedSize());
       build.cleanUp();
       System.out.println(build.estimatedSize());


   }
}
